#include "FanInt.h"

#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <fan-llvm/core/Core.h>
#include "ArgErr.h"
#include "CastErr.h"
#include "DateTime.h"
#include "Decimal.h"
#include "Duration.h"
#include "Func.h"
#include "ParseErr.h"
#include "Range.h"
#include "Str.h"
#include "TimeZone.h"
#include "Type.h"
#include "UnsupportedErr.h"


static Int kMinIntVal;
static Int kMaxIntVal;
static Str* kMinIntStr;
static Str* kHexPrefixStr;

Type* Int_classType = NULL;
VTable* Int_classVTable = NULL;


/**********************************************
 *
 * Methods and fields
 *
 **********************************************/


/* Bool equals(Obj? obj) */
Bool Int_equals(Obj self, NullableObj obj)
{
  if (!VAL_IS_NULL(obj) && Obj_typeof(obj) == Int_classType)
    return (VAL_GET_INT(self) == VAL_GET_INT(obj));
  else
    return FALSE;
}


/* Int compare(Obj obj) */
Int Int_compare(Obj self, Obj obj)
{
  if (Obj_typeof(obj) == Int_classType)
  {
    Int i = VAL_GET_INT(self);
    Int j = VAL_GET_INT(obj);

    if (i < j)
      return -1;
    else if (i > j)
      return 1;
    else
      return 0;
  }
  else
  {
    exceptionThrow(CastErr_newWithTypes(Obj_typeof(obj), Int_classType));
    return 0;
  }
}


/* Int hash() */
Int Int_hash(Obj self)
{
  return VAL_GET_INT(self);
}


/* Str toStr() */
Str* Int_toStr(Obj self)
{
  // Treat kMinIntVal as a special-case, as making kMinIntVal positive would overflow
  if (VAL_GET_INT(self) == kMinIntVal)
    return kMinIntStr;

  Int val = VAL_GET_INT(self);
  Int absVal = (val < 0) ? -val : val;
  Bool isNeg = (val < 0);

  // Determine the size of the buffer
  Int bufSize = 0;
  if (val == 0)
    bufSize = 1;
  else
  {
    if (isNeg)
      bufSize++;
    for (Int v = absVal; v != 0; v /= 10)
      bufSize++;
  }

  // Fill the buffer with digits
  Char buf[32];
  if (val == 0)
    buf[0] = '0';
  else
  {
    if (isNeg)
      buf[0] = '-';
    Int i = bufSize - 1;
    for (Int v = absVal; v != 0; v /= 10)
    {
      buf[i] = '0' + (v % 10);
      i--;
    }
  }

  return Str_newFromBuffer(buf, bufSize);
}


/* Int defVal */
Int Int_defVal(void)
{
  return 0;
}


/* Int maxVal */
Int Int_maxVal(void)
{
  return kMaxIntVal;
}


/* Int minVal */
Int Int_minVal(void)
{
  return kMinIntVal;
}


/* Int? fromStr(Str s, Int radix, Bool checked) */
NullableInt Int_fromStr_1(Str* s)
{
  return Int_fromStr(s, 10, TRUE);
}


/* Int? fromStr(Str s, Int radix, Bool checked) */
NullableInt Int_fromStr_2(Str* s, Int radix)
{
  return Int_fromStr(s, radix, TRUE);
}


/* Int? fromStr(Str s, Int radix, Bool checked) */
NullableInt Int_fromStr(Str* s, Int radix, Bool checked)
{
  Bool isNegative = FALSE;
  Bool isValid = FALSE;
  uint64_t absVal = 0;

  for (Int i = 0; i < s->len; i++)
  {
    Char ch = s->buffer[i];
    Char lowerCh = ch | 0x20;
    Int num = 0;

    if (i == 0 && radix == 10 && ch == '-')
      isNegative = TRUE;
    else if ('0' <= ch && ch <= '9' && (ch - '0') < radix)
      num = ch - '0';
    else if ('a' <= lowerCh && lowerCh <= 'z' && (lowerCh - 'a') < (radix - 10))
      num = lowerCh - 'a' + 10;
    else
    {
      isValid = FALSE;
      break;
    }

    isValid = TRUE;
    absVal = (absVal * radix) + num;
  }

  if (!isValid)
  {
    if (checked)
      exceptionThrow(ParseErr_newWithFormattedMsg("Invalid Int: '%r'", s));
    else
      return VAL_NULL;
  }

  Int val = (int64_t)absVal;
  return VAL_MAKE_INT(isNegative ? -val : val);
}


/* Int random(Range? r) */
Int Int_random_0(void)
{
  return Int_random(NULL);
}


/* Int random(Range? r) */
Int Int_random(Range* r)
{
  Int randVal;
#ifdef INT_IS_64BIT
  int32_t lsb = ((rand() & 0xFF) << 16) | (rand() & 0xFF);
  int32_t msb = ((rand() & 0xFF) << 16) | (rand() & 0xFF);
  randVal = ((int64_t)lsb << 32) | (int64_t)msb;
#else
  randVal = ((rand() & 0xFF) << 16) | (rand() & 0xFF);
#endif

  if (r == NULL)
    return randVal;
  else
  {
    if (randVal < 0)
      randVal = -randVal;
    Int start = r->start;
    Int end = r->end;
    if (!r->exclusive)
      end++;
    if (end <= start)
      exceptionThrow(ArgErr_newWithFormattedMsg("Range end < start: %r", r));
    return (start + (randVal % (end - start)));
  }
}


/* Int negate() */
Int Int_negate(Int self)
{
  return -self;
}


/* Int increment() */
Int Int_increment(Int self)
{
  return self + 1;
}


/* Int decrement() */
Int Int_decrement(Int self)
{
  return self - 1;
}


/* Int mult(Int b) */
Int Int_mult(Int self, Int b)
{
  return self * b;
}


/* Float multFloat(Float b) */
Float Int_multFloat(Int self, Float b)
{
  return self * b;
}


/* Decimal multDecimal(Decimal b) */
Decimal* Int_multDecimal(Int self, Decimal* b)
{
  return Decimal_mult(Num_toDecimal(VAL_MAKE_INT(self)), b);
}


/* Int div(Int b) */
Int Int_div(Int self, Int b)
{
  return self / b;
}


/* Float divFloat(Float b) */
Float Int_divFloat(Int self, Float b)
{
  return self / b;
}


/* Decimal divDecimal(Decimal b) */
Decimal* Int_divDecimal(Int self, Decimal* b)
{
  return Decimal_div(Num_toDecimal(VAL_MAKE_INT(self)), b);
}


/* Int mod(Int b) */
Int Int_mod(Int self, Int b)
{
  // Note: Works because we're in C99.
  // Otherwise, with negative numbers, result would depend on the compiler
  return self % b;
}


/* Float modFloat(Float b) */
Float Int_modFloat(Int self, Float b)
{
#ifdef FLOAT_IS_64BIT
  return fmod((Float)self, b);
#else
  return fmodf((Float)self, b);
#endif
}


/* Decimal modDecimal(Decimal b) */
Decimal* Int_modDecimal(Int self, Decimal* b)
{
  return Decimal_mod(Num_toDecimal(VAL_MAKE_INT(self)), b);
}


/* Int plus(Int b) */
Int Int_plus(Int self, Int b)
{
  return self + b;
}


/* Float plusFloat(Float b) */
Float Int_plusFloat(Int self, Float b)
{
  return self + b;
}


/* Decimal plusDecimal(Decimal b) */
Decimal* Int_plusDecimal(Int self, Decimal* b)
{
  return Decimal_plus(Num_toDecimal(VAL_MAKE_INT(self)), b);
}


/* Int minus(Int b) */
Int Int_minus(Int self, Int b)
{
  return self - b;
}


/* Float minusFloat(Float b) */
Float Int_minusFloat(Int self, Float b)
{
  return self - b;
}


/* Decimal minusDecimal(Decimal b) */
Decimal* Int_minusDecimal(Int self, Decimal* b)
{
  return Decimal_minus(Num_toDecimal(VAL_MAKE_INT(self)), b);
}


/* Int not() */
Int Int_not(Int self)
{
  return ~self;
}


/* Int and(Int b) */
Int Int_and(Int self, Int b)
{
  return self & b;
}


/* Int or(Int b) */
Int Int_or(Int self, Int b)
{
  return self | b;
}


/* Int xor(Int b) */
Int Int_xor(Int self, Int b)
{
  return self ^ b;
}


/* Int shiftl(Int b) */
Int Int_shiftl(Int self, Int b)
{
  return self << b;
}


/* Int shiftr(Int b) */
Int Int_shiftr(Int self, Int b)
{
  return self >> b;
}


/* Int abs() */
Int Int_abs(Int self)
{
  return (self >= 0) ? self : -self;
}


/* Int min(Int that) */
Int Int_min(Int self, Int that)
{
  return (self <= that) ? self : that;
}


/* Int max(Int that) */
Int Int_max(Int self, Int that)
{
  return (self >= that) ? self : that;
}


/* Int pow(Int pow) */
Int Int_pow(Int self, Int pow)
{
  if (pow >= 0)
  {
    Int res = 1;
    while (pow > 0)
    {
      if ((pow & 1) != 0)
        res *= self;
      pow >>= 1;
      self *= self;
    }
    return res;
  }
  else
  {
    exceptionThrow(ArgErr_newWithFormattedMsg("pow < 0"));
    return 0;
  }
}


/* Bool isEven() */
Bool Int_isEven(Int self)
{
  return ((self & 1) == 0);
}


/* Bool isOdd() */
Bool Int_isOdd(Int self)
{
  return ((self & 1) != 0);
}


/* Bool isSpace() */
Bool Int_isSpace(Int self)
{
  return (self == ' ' || self == '\n' || self == '\t' || self == '\r' || self == '\f');
}


/* Bool isAlpha() */
Bool Int_isAlpha(Int self)
{
  Int lowerCh = self | 0x20;
  return ('a' <= lowerCh && lowerCh <= 'z');
}


/* Bool isAlphaNum() */
Bool Int_isAlphaNum(Int self)
{
  Int ch = self;
  Int lowerCh = ch | 0x20;
  return (('a' <= lowerCh && lowerCh <= 'z') || ('0' <= ch && ch <= '9'));
}


/* Bool isUpper() */
Bool Int_isUpper(Int self)
{
  return ('A' <= self && self <= 'Z');
}


/* Bool isLower() */
Bool Int_isLower(Int self)
{
  return ('a' <= self && self <= 'z');
}


/* Int upper() */
Int Int_upper(Int self)
{
  if ('a' <= self && self <= 'z')
    return self & ~0x20;
  else
    return self;
}


/* Int lower() */
Int Int_lower(Int self)
{
  if ('A' <= self && self <= 'Z')
    return self | 0x20;
  else
    return self;
}


/* Bool isDigit(Int radix) */
Bool Int_isDigit_0(Int self)
{
  return ('0' <= self && self <= '9');
}


/* Bool isDigit(Int radix) */
Bool Int_isDigit(Int self, Int radix)
{
  if (radix <= 10)
    return ('0' <= self && self < ('0' + radix));
  else if (radix <= 36)
  {
    if ('0' <= self && self <= '9')
      return TRUE;
    Int lowerCh = self | 0x20;
    return ('a' <= lowerCh && lowerCh < ('a' + radix - 10));
  }
  return FALSE;
}


/* Int? toDigit(Int radix) */
NullableInt Int_toDigit_0(Int self)
{
  return Int_toDigit(self, 10);
}


/* Int? toDigit(Int radix) */
NullableInt Int_toDigit(Int self, Int radix)
{
  if (0 <= self && self < radix && self < 36)
  {
    if (self < 10)
      return VAL_MAKE_INT('0' + self);
    else
      return VAL_MAKE_INT('a' + self - 10);
  }
  else
    return VAL_NULL;
}


/* Int? fromDigit(Int radix) */
NullableInt Int_fromDigit_0(Int self)
{
  return Int_fromDigit(self, 10);
}


/* Int? fromDigit(Int radix) */
NullableInt Int_fromDigit(Int self, Int radix)
{
  Int ch = self;
  Int lowerCh = ch | 0x20;

  Int val;
  if ('0' <= ch && ch <= '9')
    val = ch - '0';
  else if ('a' <= lowerCh && lowerCh <= 'z')
    val = ch - 'a' + 10;
  else
    return VAL_NULL;

  return (val < radix) ? VAL_MAKE_INT(val) : VAL_NULL;
}


/* Bool equalsIgnoreCase(Int ch) */
Bool Int_equalsIgnoreCase(Int self, Int ch)
{
  if ('A' <= self && self <= 'Z')
    self |= 0x20;
  if ('A' <= ch && ch <= 'Z')
    ch |= 0x20;
  return (self == ch);
}


/* Str toLocale(Str? pattern) */
Str* Int_toLocale_0(Int self)
{
  return Int_toLocale(self, NULL);
}


/* Str toLocale(Str? pattern) */
Str* Int_toLocale(Int self, Str* pattern)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Int.toLocale not implemented yet"));
  return NULL;
}


/* Bool localeIsUpper() */
Bool Int_localeIsUpper(Int self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Int.localeIsUpper not implemented yet"));
  return FALSE;
}


/* Bool localeIsLower() */
Bool Int_localeIsLower(Int self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Int.localeIsLower not implemented yet"));
  return FALSE;
}


/* Int localeUpper() */
Int Int_localeUpper(Int self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Int.localeUpper not implemented yet"));
  return FALSE;
}


/* Int localeLower() */
Int Int_localeLower(Int self)
{
  exceptionThrow(UnsupportedErr_newWithFormattedMsg("sys::Int.localeLower not implemented yet"));
  return FALSE;
}


/* Str toHex(Int? width) */
Str* Int_toHex_0(Int self)
{
  return Int_toHex(self, VAL_NULL);
}


/* Str toHex(Int? width) */
Str* Int_toHex(Int self, NullableInt width)
{
  // Determine the size of the buffer
  uint64_t val = self;
  Int bufSize = 0;
  if (val == 0)
    bufSize = 1;
  else
  {
    for (uint64_t v = val; v != 0; v >>= 4)
      bufSize++;
  }

  // Fill the buf with hex-digits
  Char buf[32];
  if (val == 0)
    buf[0] = '0';
  else
  {
    Int i = bufSize - 1;
    for (uint64_t v = val; v != 0; v >>= 4)
    {
      uint8_t digit = v & 0x0F;
      if (digit < 10)
        buf[i] = '0' + digit;
      else
        buf[i] = 'a' + digit - 10;
      i--;
    }
  }

  // Determine the number of zeros to add
  Int widthInt = !VAL_IS_NULL(width) ? VAL_GET_INT(width) : 0;
  widthInt = (bufSize > widthInt) ? bufSize : widthInt;
  Int numZeros = widthInt - bufSize;

  // Build a new Str with the zeros and the hex-digits
  Str* hexStr = Str_newUninitialized(widthInt);
  for (Int i = 0; i < numZeros; i++)
    hexStr->buffer[i] = '0';
  for (Int i = 0; i < bufSize; i++)
    hexStr->buffer[numZeros + i] = buf[i];

  return hexStr;
}


/* Str toChar() */
Str* Int_toChar(Int self)
{
  Str* chStr = Str_newUninitialized(1);
  chStr->buffer[0] = self;
  return chStr;
}


/* Str toCode(Int base) */
Str* Int_toCode_0(Int self)
{
  return Int_toCode(self, 10);
}


/* Str toCode(Int base) */
Str* Int_toCode(Int self, Int base)
{
  if (base == 10)
    return Int_toStr(VAL_MAKE_INT(self));
  else if (base == 16)
    return Str_plus(kHexPrefixStr, VAL_MAKE_REF(Int_toHex(self, VAL_NULL)));
  else
  {
    exceptionThrow(ArgErr_newWithFormattedMsg("Invalid base: %i", base));
    return NULL;
  }
}


/* Duration toDuration() */
Duration* Int_toDuration(Int self)
{
  return Duration_make(self);
}


/* DateTime toDateTime(TimeZone tz) */
DateTime* Int_toDateTime_0(Int self)
{
  return Int_toDateTime(self, TimeZone_cur());
}


/* DateTime toDateTime(TimeZone tz) */
DateTime* Int_toDateTime(Int self, TimeZone* tz)
{
  return DateTime_makeTicks(self, tz);
}


/* Void times(|Int->Void| c) */
void Int_times(Int self, Func* c)
{
  for (Int i = 0; i < self; i++)
    FUNC_VTABLE(c)->call_1(c, VAL_MAKE_INT(i));
}


/**********************************************
 *
 * Type-system functions
 *
 **********************************************/


Type* Int_initType(void)
{
  if (Int_classType == NULL)
  {
    Type* baseType = Num_initType();
    VTable* vtable = Int_initVTable();
    Type* type = Type_new("Int", baseType, vtable, sizeof(FanInt), sizeof(FanIntVTable));
    Type_registerMethodImplementation(type, "abs", Int_abs, 0);
    Type_registerMethodImplementation(type, "and", Int_and, 1);
    Type_registerMethodImplementation(type, "compare", Int_compare, 1);
    Type_registerMethodImplementation(type, "decrement", Int_decrement, 0);
    Type_registerMethodImplementation(type, "div", Int_div, 1);
    Type_registerMethodImplementation(type, "divDecimal", Int_divDecimal, 1);
    Type_registerMethodImplementation(type, "divFloat", Int_divFloat, 1);
    Type_registerMethodImplementation(type, "equals", Int_equals, 1);
    Type_registerMethodImplementation(type, "equalsIgnoreCase", Int_equalsIgnoreCase, 1);
    Type_registerMethodImplementation(type, "fromDigit", Int_fromDigit_0, 0);
    Type_registerMethodImplementation(type, "fromDigit", Int_fromDigit, 1);
    Type_registerMethodImplementation(type, "fromStr", Int_fromStr_1, 1);
    Type_registerMethodImplementation(type, "fromStr", Int_fromStr_2, 2);
    Type_registerMethodImplementation(type, "fromStr", Int_fromStr, 3);
    Type_registerMethodImplementation(type, "hash", Int_hash, 0);
    Type_registerMethodImplementation(type, "increment", Int_increment, 0);
    Type_registerMethodImplementation(type, "isAlpha", Int_isAlpha, 0);
    Type_registerMethodImplementation(type, "isAlphaNum", Int_isAlphaNum, 0);
    Type_registerMethodImplementation(type, "isDigit", Int_isDigit_0, 0);
    Type_registerMethodImplementation(type, "isDigit", Int_isDigit, 1);
    Type_registerMethodImplementation(type, "isEven", Int_isEven, 0);
    Type_registerMethodImplementation(type, "isLower", Int_isLower, 0);
    Type_registerMethodImplementation(type, "isOdd", Int_isOdd, 0);
    Type_registerMethodImplementation(type, "isSpace", Int_isSpace, 0);
    Type_registerMethodImplementation(type, "isUpper", Int_isUpper, 0);
    Type_registerMethodImplementation(type, "localeIsLower", Int_localeIsLower, 0);
    Type_registerMethodImplementation(type, "localeIsUpper", Int_localeIsUpper, 0);
    Type_registerMethodImplementation(type, "localeLower", Int_localeLower, 0);
    Type_registerMethodImplementation(type, "localeUpper", Int_localeUpper, 0);
    Type_registerMethodImplementation(type, "lower", Int_lower, 0);
    Type_registerMethodImplementation(type, "max", Int_max, 1);
    Type_registerMethodImplementation(type, "min", Int_min, 1);
    Type_registerMethodImplementation(type, "minus", Int_minus, 1);
    Type_registerMethodImplementation(type, "minusDecimal", Int_minusDecimal, 1);
    Type_registerMethodImplementation(type, "minusFloat", Int_minusFloat, 1);
    Type_registerMethodImplementation(type, "mod", Int_mod, 1);
    Type_registerMethodImplementation(type, "modDecimal", Int_modDecimal, 1);
    Type_registerMethodImplementation(type, "modFloat", Int_modFloat, 1);
    Type_registerMethodImplementation(type, "mult", Int_mult, 1);
    Type_registerMethodImplementation(type, "multDecimal", Int_multDecimal, 1);
    Type_registerMethodImplementation(type, "multFloat", Int_multFloat, 1);
    Type_registerMethodImplementation(type, "negate", Int_negate, 0);
    Type_registerMethodImplementation(type, "not", Int_not, 0);
    Type_registerMethodImplementation(type, "or", Int_or, 1);
    Type_registerMethodImplementation(type, "plus", Int_plus, 1);
    Type_registerMethodImplementation(type, "plusDecimal", Int_plusDecimal, 1);
    Type_registerMethodImplementation(type, "plusFloat", Int_plusFloat, 1);
    Type_registerMethodImplementation(type, "pow", Int_pow, 1);
    Type_registerMethodImplementation(type, "random", Int_random_0, 0);
    Type_registerMethodImplementation(type, "random", Int_random, 1);
    Type_registerMethodImplementation(type, "shiftl", Int_shiftl, 1);
    Type_registerMethodImplementation(type, "shiftr", Int_shiftr, 1);
    Type_registerMethodImplementation(type, "times", Int_times, 1);
    Type_registerMethodImplementation(type, "toChar", Int_toChar, 0);
    Type_registerMethodImplementation(type, "toCode", Int_toCode_0, 0);
    Type_registerMethodImplementation(type, "toCode", Int_toCode, 1);
    Type_registerMethodImplementation(type, "toDateTime", Int_toDateTime_0, 0);
    Type_registerMethodImplementation(type, "toDateTime", Int_toDateTime, 1);
    Type_registerMethodImplementation(type, "toDigit", Int_toDigit_0, 0);
    Type_registerMethodImplementation(type, "toDigit", Int_toDigit, 1);
    Type_registerMethodImplementation(type, "toDuration", Int_toDuration, 0);
    Type_registerMethodImplementation(type, "toHex", Int_toHex_0, 0);
    Type_registerMethodImplementation(type, "toHex", Int_toHex, 1);
    Type_registerMethodImplementation(type, "toLocale", Int_toLocale_0, 0);
    Type_registerMethodImplementation(type, "toLocale", Int_toLocale, 1);
    Type_registerMethodImplementation(type, "toStr", Int_toStr, 0);
    Type_registerMethodImplementation(type, "upper", Int_upper, 0);
    Type_registerMethodImplementation(type, "xor", Int_xor, 1);
    Type_registerFieldImplementation(type, "defVal", Int_defVal, NULL);
    Type_registerFieldImplementation(type, "maxVal", Int_maxVal, NULL);
    Type_registerFieldImplementation(type, "minVal", Int_minVal, NULL);
    Int_classType = type;
    Int_initStatic();
  }
  return Int_classType;
}


VTable* Int_initVTable(void)
{
  if (Int_classVTable == NULL)
  {
    VTable* vtable = memoryAlloc(sizeof(FanIntVTable));
    memcpy(vtable, Num_initVTable(), sizeof(FanNumVTable));
    ((FanObjVTable*)vtable)->compare = Int_compare;
    ((FanObjVTable*)vtable)->equals = Int_equals;
    ((FanObjVTable*)vtable)->hash = Int_hash;
    ((FanObjVTable*)vtable)->toStr = Int_toStr;
    Int_classVTable = vtable;
  }
  return Int_classVTable;
}


void Int_initStatic(void)
{
#ifdef INT_IS_64BIT
  kMinIntVal = INT64_MIN;
  kMaxIntVal = INT64_MAX;
  kMinIntStr = Str_newFromCStr("-9223372036854775808");
 #else
  kMinIntVal = INT32_MIN;
  kMaxIntVal = INT32_MAX;
  kMinIntStr = Str_newFromCStr("-2147483648");
 #endif
  kHexPrefixStr = Str_newFromCStr("0x");

  srand(time(NULL));
}
